#pragma once
#include "Err.hpp"
#include "Log.hpp"

#include <string>

#include <cstdlib>
#include <cstring>

#include <sys/socket.h>
#include <sys/fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>

const static int defaultfd = -1;
const static int gbacklog = 5;

class Sock
{
public:
    Sock() : sockfd_(defaultfd)
    {
    }

    void Socket() // 创建套接字
    {
        sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd_ < 0)
        {
            logMessage(Fatal, "sockfd create error:%s,[code:%d]", strerror(errno), errno);
            exit(SOCKET_ERR);
        }
        logMessage(Info, "socket create sucessfully");
        // 设置地址是复用的，即端口号是复用的
        int opt = 1;
        int ret = setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
        if (ret < 0)
        {
            logMessage(Error, "setsockopt error:%s,[code:%d]", strerror(errno), errno);
        }
        logMessage(Info, "setsockopt sucessfully");
    }

    void Bind(uint16_t port)
    {
        struct sockaddr_in loacal;
        memset(&loacal, 0, sizeof(loacal));
        loacal.sin_family = AF_INET;
        loacal.sin_port = htons(port);
        loacal.sin_addr.s_addr = INADDR_ANY;
        int ret = bind(sockfd_, reinterpret_cast<const struct sockaddr *>(&loacal), sizeof(loacal));
        if (ret < 0)
        {
            logMessage(Fatal, "sockfd bind error:%s,[code:%d]", strerror(errno), errno);
            exit(BIND_ERR);
        }
        logMessage(Info, "sock bind sucessfully");
    }

    void Listen()
    {
        int ret = listen(sockfd_, gbacklog);
        if (ret < 0)
        {
            logMessage(Fatal, "sockfd listen error:%s,[code:%d]", strerror(errno), errno);
            exit(LISTEN_ERR);
        }
        logMessage(Info, "sock listen sucessfully");
    }

    int Accept(std::string *clinetip, uint16_t *clientport)
    {
        struct sockaddr_in client;
        memset(&client, 0, sizeof(client));
        socklen_t len = sizeof(client);
        int sockfd = accept(sockfd_, reinterpret_cast<struct sockaddr *>(&client), &len);
        if (sockfd < 0)
        {
            logMessage(Warning, "sockfd accept error:%s,[code:%d]", strerror(errno), errno);
            return -1;
        }
        *clinetip = inet_ntoa(client.sin_addr);
        *clientport = ntohs(client.sin_port);
        logMessage(Info, "sock accept sucessfully,clientip:%s, clientport:%d", clinetip->c_str(), *clientport);
        return sockfd;
    }

    void Connect(const std::string &serverip, const uint16_t &serverport)
    {
        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(serverport);
        server.sin_addr.s_addr = inet_addr(serverip.c_str());
        int ret = connect(sockfd_, (struct sockaddr *)&server, sizeof(server));
        if (ret < 0)
        {
            logMessage(Fatal, "sockfd connect error:%s,[code:%d]", strerror(errno), errno);
            exit(CONNECT_ERR);
        }
        logMessage(Info, "sock connect sucessfully");
    }

    void Close()
    {
        if (sockfd_ != defaultfd)
        {
            close(sockfd_);
        }
    }

    int Fd()
    {
        return sockfd_;
    }

    ~Sock()
    {
    }

private:
    int sockfd_;
};